﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Elsa.Extensions;
using Elsa.JavaScript.Models;
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Elsa.Workflows.Serialization.Serializers;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using T.FAS.Runtime.MF.Activities;
using T.FAS.Runtime.MF.Activities.Helper;
using T.FAS.Runtime.MF.Entity;
using T.FAS.Runtime.MF.SPI;
using ElsaExpression = Elsa.Expressions.Models.Expression;

namespace T.FAS.Runtime.MF.Activities.Nodes
{
    internal class LLMNode : NodeBase, INodeMapper
    {
        [JsonProperty("data")]
        public new LLMNodeData Data { get; set; }
        public List<Variable> Variables { get; set; } = new List<Variable>();
        public List<OutputDefinition> Outputs { get; set; }
        public List<InputDefinition> Inputs { get; set; } = new List<InputDefinition>();
        public Dictionary<string, Dictionary<string, string>> PromptOptionConfigs = new Dictionary<string, Dictionary<string, string>>
        {
            {"qwq:32b",new Dictionary<string, string>
                {
                    { "temperature","temperature"},{ "max_tokens","max_tokens"},{ "top_p","top_p"},{ "frequencyPenalty","frequencyPenalty"},{ "response_format","response_format"},
                }
            },
            {"qwenMax",new Dictionary<string, string>
                {
                    { "temperature","temperature"},{ "max_tokens","max_tokens"},{ "top_p","top_p"},{ "frequencyPenalty","frequencyPenalty"},{ "response_format","response_format"},
                }
            },
        };

        public override List<UserVariable> NodeInputOutputVariables
        {
            get
            {
                if (Data.OutputVariables == null) return new List<UserVariable>();
                Data.OutputVariables.ForEach(v =>
                {
                    SetVariableInfo(v);
                });
                return Data.OutputVariables;
            }
        }
        public IActivity MapToActivity()
        {
            var activity = new LLMActivity() { Id = Id, Name = Id };
            activity.LLMModel = Data.Model.Name;
            //大模型配置选项
            ProcessLLMOptions(activity);
            //提示词
            ProcessPrompts(activity);
            //节点变量
            ProcessPromptVariables(activity);
            //处理返回变量
            ProcessOutputVariables(activity);
            //失败策略
            activity.FaultedStrategy = "节点停止运行";
            //失败重试
            if (Data.RetryConfig != null)
            {
                activity.MaxRetryCont = Data.RetryConfig.MaxRetries;
                activity.DelaySeconds = Data.RetryConfig.RetryDelay;
            }
           

            return activity;
        }
        private void ProcessOutputVariables(LLMActivity activity)
        {
            if (Data.OutputVariables == null)
            {
                Data.OutputVariables = new List<UserVariable>();
                Data.OutputVariables.Add(new UserVariable() { VariableName = "text",Type="string" });
                Data.OutputVariables.Add(new UserVariable() { VariableName = "usage", Type = "object" });
            }
            var activityOutputVariables = new List<Variable>();

            foreach (var varItem in (Data.OutputVariables ?? new List<UserVariable>()))
            {
                SetVariableInfo(varItem);
                var variable = VariableHelper.GenerateVariable(varItem);
                Variables.Add(variable);
                activityOutputVariables.Add(variable);

                //为了向迭代节点返回值，此处特意增加一个变量，用于处理节点的返回值数组
                var iterationResultVarItem = new UserVariable() { CompositeVarName = $"{varItem.CompositeVarName}{MFConst.IterationResultVariableSuffix}", Type = varItem.ValueType };
                var iterationResultVar = VariableHelper.GenerateVariable(iterationResultVarItem);
                Variables.Add(iterationResultVar);
            }
            activity.OutputVariables = activityOutputVariables;
        }
        private void SetVariableInfo(UserVariable v)
        {
            if (string.IsNullOrEmpty(v.ActivityID))
            {
                v.ActivityID = Id;
                v.CompositeVarName = $"{Id}:{v.VariableName}";
                v.CompositeVarName = v.CompositeVarName.Replace("-", "").Replace(":", "");
            }

        }
        private void ProcessPromptVariables(LLMActivity activity)
        {
            GetPromptVariables();
            ReplaceAllVariableName(Data.Variables);
            activity.PromptVariables = new List<BranchCondition>();
            foreach (var item in Data.Variables)
            {
                var promptVariableName = item.Key;
                var variable = item.Value;
                var expression = string.Empty;

                /* var varSelectors = variable.VariableName.Split('.');
                 var varItem = varSelectors[0];
                 if (varSelectors.Length > 1 || (varSelectors.Count() == 1 && variable.DataType == "record"))
                 {
                     expression = $"let {varItem} = JSON.parse(variables.{varItem});{varItem};";
                 }
                 else
                 {
                     expression = $"let {varItem} = variables.{varItem};{varItem};";
                 }*/
                expression = ActivityHelper.GetVariableExpression(variable.VariableName, variable.DataType,false);
                activity.PromptVariables.Add(new BranchCondition { Label = promptVariableName, Condition = JavaScriptExpression.Create(expression) });
            }
        }

        private void ProcessPrompts(LLMActivity activity)
        {
            if (Data.Prompts == null) return;
            activity.Prompts = new List<BranchCondition>();
            //if (!string.IsNullOrEmpty(Data.Prompts.SystemPrompt))
            //{
            //    activity.Prompts.Add(new BranchCondition { Label = $"system", Condition = ElsaExpression.LiteralExpression(Data.Prompts.SystemPrompt) });
            //}
            //if (!string.IsNullOrEmpty(Data.Prompts.UserPrompt))
            //{
            //    activity.Prompts.Add(new BranchCondition { Label = $"user", Condition = ElsaExpression.LiteralExpression(Data.Prompts.UserPrompt) });
            //}

            if (Data.Prompts != null && Data.Prompts.Count() > 0)
            {
                activity.Prompts = new List<BranchCondition>();
                foreach (var item in Data.Prompts)
                {
                    activity.Prompts.Add(new BranchCondition { Label = $"{Guid.NewGuid().ToString()}/{item.Role}", Condition = ElsaExpression.LiteralExpression(item.Text) });
                }
            }
        }

        /// <summary>
        /// 将提示词中的变量转换为运行时真实的变量值
        /// </summary>
        /// <param name="prompt"></param>
        /// <returns></returns>
        private void GetPromptVariables()
        {
            Data.Variables = new Dictionary<string, PromptVaribleNode>();
            const string pattern = @"\{\{#([^{}]+)#\}\}";
            var variables = new Dictionary<string, string>();
            if (Data.Prompts != null && Data.Prompts.Count() > 0)
            {
                foreach (var item in Data.Prompts)
                {
                    var prompt = item.Text;
                    foreach (Match match in Regex.Matches(prompt, pattern))
                    {
                        if (match.Success)
                        {
                            var variableKey = match.Groups[0].Value;
                            var variableKeyPath = match.Groups[1].Value;
                            if (!variables.ContainsKey(variableKey))
                            {
                                variables.Add(variableKey, variableKeyPath);
                            }
                        }
                    }
                }
            }

    
            foreach (var item in variables)
            {
                var variablePath = item.Value;
                var variablePaths = variablePath.Split('.');

                Data.Variables.Add(item.Key, new PromptVaribleNode { VariableName = variablePaths.Length > 1 ? variablePaths[1]: variablePaths[0], NodeID = variablePaths.Length > 1 ? variablePaths[0] : this.Id});
            }
        }

        private void ProcessLLMOptions(LLMActivity activity)
        {
            activity.LLMOptions = new List<BranchCondition>();
            if (Data.Temperature.HasValue)
            {
                
                activity.LLMOptions.Add(new BranchCondition { Label = GetPromptOptionName("temperature"), Condition = ElsaExpression.LiteralExpression(Data.Temperature.Value) });
            }
            if (Data.MaxTokens.HasValue)
            {
                activity.LLMOptions.Add(new BranchCondition { Label = GetPromptOptionName("max_tokens"), Condition = ElsaExpression.LiteralExpression(Data.MaxTokens.Value) });
            }
            if (Data.TopP.HasValue)
            {
                activity.LLMOptions.Add(new BranchCondition { Label = GetPromptOptionName("top_p"), Condition = ElsaExpression.LiteralExpression(Data.TopP.Value) });
            }
            if (Data.FrequencyPenalty.HasValue)
            {
                activity.LLMOptions.Add(new BranchCondition { Label = GetPromptOptionName("frequencyPenalty"), Condition = ElsaExpression.LiteralExpression(Data.FrequencyPenalty.Value) });
            }
            if (!string.IsNullOrEmpty(Data.OutputParser))
            {
                activity.LLMOptions.Add(new BranchCondition { Label = GetPromptOptionName("response_format"), Condition = ElsaExpression.LiteralExpression(Data.OutputParser) });
            }
        }
        private string GetPromptOptionName(string optionName)
        {
            if (string.IsNullOrEmpty(Data.Model.Name)) return optionName;
            if (!PromptOptionConfigs.ContainsKey(Data.Model.Name)) return optionName;
            var promptOptions = PromptOptionConfigs[Data.Model.Name];
            if (!promptOptions.ContainsKey(optionName)) return optionName;
            return promptOptions[optionName];
        }

        /// <summary>
        /// //将表达式中的变量替换为真实的变量名称
        /// </summary>
        /// <param name="conditions"></param>
        private void ReplaceAllVariableName(Dictionary<string, PromptVaribleNode> Variables)
        {
            foreach (var variable in Variables.Values)
            {
                variable.VariableName =  ActivityHelper.GetSourceVariableRealName(this, variable.VariableName, variable.NodeID);
                //var varSelectors = variable.VariableName.Split('.');
                //var varItem = varSelectors[0];

                ////从整个工作流中获取应的变量信息
                //var varInfo = (this.BelongTo.NodeInputOutputVariables ?? new List<UserVariable>()).Find(t => t.VariableName == varItem && t.ActivityID == variable.NodeID);
                //if (varInfo == null) { continue; }
                //var varName = varInfo.CompositeVarName;
                //varSelectors[0] = varName;
                //variable.VariableName = string.Join(".", varSelectors);
            }
        }
    }
    internal class LLMNodeData
    {
        [JsonProperty("model")]
        public LLMModel Model { get; set; }
        [JsonProperty("temperature")]
        public double? Temperature { get; set; }
        [JsonProperty("maxTokens")]
        public int? MaxTokens { get; set; }

        [JsonProperty("topP")]
        public int? TopP { get; set; }
        [JsonProperty("frequencyPenalty")]
        public int? FrequencyPenalty { get; set; } = 0;
        //[JsonProperty("prompt")]
        //public PromptTemplateNode Prompts { get; set; }
        [JsonProperty("prompt_template")]
        public List<PromptTemplateNode> Prompts { get; set; }
        //[JsonProperty("context")]
        //public ContextNode_back Context { get; set; }
        [JsonProperty("outputParser")]
        public string OutputParser { get; set; }
        [JsonProperty("jsonSchema")]
        public string JsonSchema { get; set; }
        [JsonProperty("variables")]
        public Dictionary<string, PromptVaribleNode> Variables { get; set; }
        [JsonProperty("timeout")]
        public string Timeout { get; set; }
        [JsonProperty("label")]
        public string Label { get; set; }
        [JsonProperty("description")]
        public string Description { get; set; }
        [JsonProperty("status")]
        public string Status { get; set; }
        [JsonProperty("retryConfig")]
        public RetryConfigNode RetryConfig { get; set; }

        [JsonProperty("outputFields")]
        public List<UserVariable> OutputVariables { get; set; }
    }
    internal class LLMModel
    {
        [JsonProperty("provider")]
        public string Provider { get; set; }
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("mode")]
        public string Mode { get; set; }
        [JsonProperty("completion_params")]
        public Dictionary<string,object> CompletionParams { get; set; }
    }
    internal class PromptTemplateNode
    {
        [JsonProperty("id")]
        public string ID { get; set; }
        [JsonProperty("role")]
        public string Role { get; set; }
        [JsonProperty("text")]
        public string Text { get; set; }

    }
    internal class RetryConfigNode
    {
        [JsonProperty("maxRetries")]
        public int MaxRetries { get; set; }
        [JsonProperty("retryDelay")]
        public int RetryDelay { get; set; }

    }
    internal class PromptVaribleNode
    {
        [JsonProperty("nodeId")]
        public string NodeID { get; set; }
        [JsonProperty("dataType")]
        public string DataType { get; set; }

        [JsonProperty("variable")]
        public string VariableName { get; set; }
        [JsonProperty("variableId")]
        public string VariableID { get; set; }

    }
}
